第3週 制御構文
本日の内容
前回の復習
制御構文とは
主な制御構文
if文
switch文
while文
do-while文
for文
三項演算子
条件式
前回の復習
char型の変数を宣言した後に、printf("%d",(char))を使って値を表示してください。また、なぜそうなるのか考察してください。
code:assign.c
int main() {
char num1 = 'a';
int num2 = num1;
printf("%d",num2);//97
return 0;
}
code:assign.c
int main() {
char num1 = 'b';
int num2 = num1;
printf("%d",num2);//98
return 0;
}
(環境などによって結果が変わることがありますが)、小文字のa,bを数値として出力すると、97,98と連続した値が表示されます。
コンピュータ上で数値以外の文字は、「文字コード」と呼ばれる数値で管理されています。char型は文字そのものを変数内に保持しているわけではなく、その文字に対応した文字コードを保持していることをこの結果は示しています。
0. 制御構文とは
プログラムの実行中に処理を分岐、繰り返しさせる事ができる構文のこと
これを用いることにより、より複雑な処理を行うことができる
1. 主な制御構文
1-1if文
分岐構造(基本形)
code:defaultif.c
if(条件式) {
処理1;
} else {
処理2;
}
条件式を満たすと処理1を実行する。条件式を満たさないと処理2を実行する。
code:defaultifonly.c
if(条件式) {
処理1;
}
条件式を満たすと処理1を実行する。条件式を満たさないと何も実行しない。
多分岐構造
code:multif.c
if(条件式1) {
処理1;
} else if(条件式2){
処理2;
} else if(条件式3){
・
・
・
} else {
処理x;
}
条件式1を満たすと処理1を実行する。条件式1を満たさないで条件式2を満たすと処理2を実行する...
全ての条件を満たさないと文xを実行する
余談
if文の条件式の部分の式を述語(predicate)と呼ぶことがあります。
1-2switch文
code:switch.c
switch(条件式) {
case 定数1:
処理;
break;
case 定数2:
処理;
break;
・
・
・
default:
処理;
break;
}
条件式を評価した値と等しいラベルへと移り,その後ろに書かれた文が順次実行される.
defaultという条件はcaseで出てきたいずれの条件にも当てはまらない場合に実行される.
break文でswitch文の実行を終了.
1-1if文は,ある条件の判定結果に応じてプログラムの流れを2つに分岐するプログラムであったが,switch文を用いると一度に複数に分岐することが出来る.
case 定数1 : のようなものをラベル(プログラムの飛び先を示す目印)という.
余談
制御構文例でのswitch文の例のように,case文の処理の後にbreakを入れない処理をフォールスルーといいます. 複数のcase文で同じ処理をさせたい時,例えば変数aが1,2,3の場合と4,5の場合でそれぞれ同じ処理をさせたい時などに使用します.
1-3while文
code:while.c
while(継続条件) {
処理;
}
継続条件がfalseでない限り,処理を繰り返し実行する.※falseは_Bool型であり、内部処理的には0(stdbool.hをincludeすることでbool型となる)
while(1) ...で処理内容を無限ループさせる記法はよく用いる。
1-4for文
code:for.c
for(初期化式; 継続条件; 値変化) {
処理;
}
①初期化式を評価・実行する.
②継続条件がfalseでなければ,処理を実行する.
③処理の実行後に値変化を行い,継続条件を評価・実行して②に戻る.
基本的にはfor(int i=0; i<n; i++){}でn回ループするときによく用いるが、初期化式と継続条件、更新条件を自由に書けるのでさまざまな利用方法がある。
table:ループでよく書く書き方
for(int i=0; i<n;i++) i=0~n-1のn回ループ
for(int i=0;i<=n;i++) iを0~nのn+1回で回すループ
for(int i=n; i>=0;i--) iをnから回すループ
for(;;) 無限ループ
for(;i>0;) while(i>0)と同値
for(char *c = str; *c!='\0'; c++) 文字列strを先頭から終端文字まで回すループ
余談
for文ではよくインクリメントおよびデクリメントという処理を行う.
table:インクリメント/デクリメント
i++; インクリメント(後置) 変数iの値を1増加させる i=i+1
++i; インクリメント(前置) 変数iの値を1増加させる i+=1
i--; デクリメント(後置) 変数iの値を1減少させる i=i-1
--i; デクリメント(前置) 変数iの値を1減少させる i-=1
この表では一見後置と前置で全く同じように見えるが,それぞれ意味が異なる.
前置が演算の終了後に左辺の値が代入されるのに対し,後置は代入の後に演算を行う.
インクリメントを例に取ると,以下のようになる.
code:increment.c
a=i++; // a=i;の後i+=1;
a=++i; // i+=1;の後a=i;
1-5do-while文
code:do.c
do {
処理;
} while(継続条件);
継続条件がfalseでない限り,処理を繰り返し実行する.
前述のwhile文とは異なり繰り返しの継続条件をループ本体の実行前ではなく,実行後に判定する.
余談
doキーワードはCで言語よりもはるかに古い時代のプログラミング言語でループを書く際に用いられていました。現在はfor文に取って代わられています。do-whileのdoはその名残でしょう。
初出は(多分)FORTRANです。現在残っている最も古い形のdoループはおそらくCommon Lispのdoでしょう。以下のように書きます。
code:do.lisp
(do (varlist)
(endlist)
&body)
varlistのところに好きなだけ変数を宣言でき、endlistの部分に終了条件を記述します。&bodyは本体です。
code:do_sample.lisp
(do ((i 0 (+ i 1))) ;iを初期値0で宣言、更新は1ループにつき1加算
((= i 10) 100) ;終了条件はi==10,終了時のこの式の返り値は100
(print i)) ;1ループに付き一回iの値を出力
2. 三項演算子
条件演算子(conditional operator)とも言われる。他にもinline if (iif)、ternary ifという呼び方がある。
名前が表すとおり、if文の処理を完結に書いたものと考えて良い。
余談
正確には、三項演算子は式であるため条件に合う方の値を返す演算子であると言える。
他言語では、if文ではなくif式が存在し、三項演算子の役割をif文が備えている言語も多い。
code:Rustの場合.rs
let a = if true { 3 } else { 4 };
table: 基本形
if文 三項演算子
if(cond) {A;} else {B;} cond ? A : B;
code:three_term.c
int a = 1;
int b = 3;
int c = 5;
int x;
x = a ? b : c;
/*上記のものと下のif文はほぼ同義*/
if(a){
x = b;
}else{
x = c;
}
}
3. 条件式
比較演算子を用いた式を条件式に組み込むことにより、より高度な処理が可能となる。
code:compare.c
int main() {
int x = 0;
int y = 1;
int z = 2;
printf("%s\n", y > 0 ? "True" : "False"); //=> True
for(int i = 0; i < 50; i++)
{
switch(i % 3){
case 0:
printf("%d", x);
x+=3;
break;
case 1:
printf("%d", y);
y+=3;
break;
case 2:
printf("%d", z);
z+=3;
break;
}
if(i % 5 == 0){
printf("i: %d, x: %d, y: %d, z: %d", i, x, y, z);
}else{
}
}
}
余談
その他,プログラムが複雑になるためあまり使われることはありませんがgoto文などがあります.
特定の処理を一箇所にまとめることができるので実際の開発ではエラー処理なんかに使われてそう
論理演算子の短絡評価を用いた疑似if文が書ける。
code:shortif.c
int main() {
int d;
scanf("%d", &d);
d || printf("d == 0\n");
return 0;
}
ここではdが0でなかった場合はd || printf..式そのものの値が真(1)であることが確定するため、C言語では右辺の評価をスキップする。そのためdが偽である場合のみ右辺を評価するような疑似if文を書くことができる。
(考察)左辺が真であるときのみ右辺を評価するには||演算子を何に変更すればよいか考え、実行してみましょう。
可読性0なのでC言語では書かないほうが良いです
第3週のまとめ
プログラムの実行中に処理を分岐、繰り返しさせる事ができる様々な制御構文を使うと複雑な処理が可能になる
第2週での比較演算子を用いた式を条件式に組み込むとより高度な処理が出来る
次回までにやってくること
練習問題
FizzBuzz問題
1〜50の整数を出力する。ただし,3で割り切れる場合は「Fizz」、5で割り切れる場合は「Buzz」、両者で割り切れる場合(すなわち15で割り切れる場合)は「Fizz Buzz」を数の代わりに出力するプログラムを作成せよ.
ループと条件分岐を上手く使いましょう。
code:出力例
./a.out
1
2
Fizz
4
Buzz
...
整数の入力を受け取ってそれが偶数であればeven、奇数であればoddと出力するプログラムを書きましょう。
scanf関数を使います。(以後多分説明しないのでここで使い方を覚えてください)
code:出力例
./a.out
4
even
./a.out
5
odd
挑戦問題
おみくじプログラムを作成しましょう。
code:出力例
./a.out
大吉です
./a.out
凶です
./a.out
吉です
alphabet
大文字かどうかは文字の比較を行うか、ctype.hヘッダのisupper/islower関数を使えば良いでしょう。
1%
コラム